// Behavior originally contributed by AknA.
//
// LICENSE:
// This work is licensed under the
//     Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
// also known as CC-BY-NC-SA.  To view a copy of this license, visit
//      http://creativecommons.org/licenses/by-nc-sa/3.0/
// or send a letter to
//      Creative Commons // 171 Second Street, Suite 300 // San Francisco, California, 94105, USA.
//

#region Summary and Documentation
// This is an variation of WaitTimer done by Nesox.
// InstanceTimer is a Quest Behavior developed to prevent that you get "You've entered too many instances".
// When you enter a instance you start the timer.
// When you have done your instance run you check the timer to see how long you have been in the instance.
// Calculated from that InstanceTimer will create a WaitTimer from that.
// 
// To start the timer use :
// <CustomBehavior File="Misc\InstanceTimer" Timer="Start" />
//
// To check how long you have been in instance and create a wait timer use:
// <CustomBehavior File="Misc\InstanceTimer" Timer="Check" />
// 
// The default wait time is 12min 30sec - the time you spent in instance.
// If you want to alter the wait time use :
// <CustomBehavior File="Misc\InstanceTimer" Timer="Check" WaitTime="10000" />
// WaitTime is in milliseconds and in above case is 10 seconds - the time you spent in instance.
//
#endregion


#region Examples
#endregion


#region Usings
using System;
using System.Collections.Generic;
using System.Windows.Media;

using Honorbuddy.QuestBehaviorCore;
using Styx.Common;
using Styx.CommonBot;
using Styx.CommonBot.Profiles;
using Styx.TreeSharp;
using Styx.WoWInternals;

using Action = Styx.TreeSharp.Action;
#endregion


namespace Styx.Bot.Quest_Behaviors {
	[CustomBehaviorFileName(@"Misc\InstanceTimer")]
	public class InstanceTimer : CustomForcedBehavior
	{
		public InstanceTimer(Dictionary<string, string> args)
		: base(args)
		{
			QBCLog.BehaviorLoggingContext = this;

			try
			{
				// QuestRequirement* attributes are explained here...
				//    http://www.thebuddyforum.com/mediawiki/index.php?title=Honorbuddy_Programming_Cookbook:_QuestId_for_Custom_Behaviors
				// ...and also used for IsDone processing.
				GoalText = GetAttributeAs("GoalText", false, ConstrainAs.StringNonEmpty, null) ?? "Waiting for {TimeRemaining}  of  {TimeDuration}";
				Timer = GetAttributeAs("Timer", true, ConstrainAs.StringNonEmpty, null) ?? "Start";
				WaitTime = GetAttributeAsNullable("WaitTime", false, ConstrainAs.Milliseconds, null) ?? 370000;
			}

			catch (Exception except)
			{
				// Maintenance problems occur for a number of reasons.  The primary two are...
				// * Changes were made to the behavior, and boundary conditions weren't properly tested.
				// * The Honorbuddy core was changed, and the behavior wasn't adjusted for the new changes.
				// In any case, we pinpoint the source of the problem area here, and hopefully it
				// can be quickly resolved.
				QBCLog.Exception(except);
				IsAttributeProblem = true;
			}
		}

		// Attributes provided by caller
		public string GoalText { get; private set; }
		public int WaitTime { get; private set; }
		public string Timer { get; private set; }

		// Private variables for internal state
		private bool _isDisposed;
		private Composite _root;
		private Common.Helpers.WaitTimer _timer;
		private Common.Helpers.WaitTimer _timeInInstance;
		private string _waitTimeAsString;

		~InstanceTimer()
		{
			Dispose(false);
		}


		public void Dispose(bool isExplicitlyInitiatedDispose)
		{
			if (!_isDisposed) {
				// NOTE: we should call any Dispose() method for any managed or unmanaged
				// resource, if that resource provides a Dispose() method.

				// Clean up managed resources, if explicit disposal...
				if (isExplicitlyInitiatedDispose) { }  // empty, for now

				// Clean up unmanaged resources (if any) here...
				TreeRoot.GoalText = string.Empty;
				TreeRoot.StatusText = string.Empty;

				// Call parent Dispose() (if it exists) here ...
				base.Dispose();
			}

			_isDisposed = true;
		}

		private string UtilSubstituteInMessage(string message)
		{
			message = message.Replace("{TimeRemaining}", UtilBuildTimeAsString(_timer.TimeLeft));
			message = message.Replace("{TimeDuration}", _waitTimeAsString);

			return (message);
		}

		private static string UtilBuildTimeAsString(TimeSpan timeSpan)
		{
			var formatString = "";
			if (timeSpan.Hours > 0) { formatString = "{0:D2}h:{1:D2}m:{2:D2}s"; }
			else if (timeSpan.Minutes > 0) { formatString = "{1:D2}m:{2:D2}s"; }
			else { formatString = "{2:D2}s"; }
			return (string.Format(formatString, timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds));
		}


		#region Overrides of CustomForcedBehavior

		protected override Composite CreateBehavior() {
			return _root ?? (_root =
				new Decorator(ret => !_timer.IsFinished,
					new Sequence(
						new Action(ret => TreeRoot.GoalText = (!string.IsNullOrEmpty(GoalText)
															? UtilSubstituteInMessage(GoalText)
															: "Waiting for timer expiration")),
						new Action(ret => TreeRoot.StatusText = "Wait time remaining... "
												+ UtilBuildTimeAsString(_timer.TimeLeft)
												+ "... of "
												+ _waitTimeAsString),
						new Action(delegate { return RunStatus.Success; })
					)
				)
			);
		}

		public override void Dispose() {
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		public override bool IsDone { get { return ((_timer != null) && _timer.IsFinished); } }

		public override void OnStart() 
		{
			// This reports problems, and stops BT processing if there was a problem with attributes...
			// We had to defer this action, as the 'profile line number' is not available during the element's
			// constructor call.
			OnStart_HandleAttributeProblem();

			if (!IsDone)
			{
				if (Timer == "Start") 
				{
					WaitTime = 0;
					Lua.DoString("StartInstanceTimerMin = date(\"%M\")");
					Lua.DoString("StartInstanceTimerSec = date(\"%S\")");
					QBCLog.Info("Started.");
				}
				if (Timer == "Check")
				{
					var startInstanceVar1 = Lua.GetReturnVal<int>("return StartInstanceTimerMin", 0);
					var startInstanceVar2 = Lua.GetReturnVal<int>("return StartInstanceTimerSec", 0);
					Lua.DoString("EndInstanceTimerMin = date(\"%M\")");
					Lua.DoString("EndInstanceTimerSec = date(\"%S\")");
					var endInstanceVar1 = Lua.GetReturnVal<int>("return EndInstanceTimerMin", 0);
					var endInstanceVar2 = Lua.GetReturnVal<int>("return EndInstanceTimerSec", 0);
					if (endInstanceVar1 < startInstanceVar1) { endInstanceVar1 = endInstanceVar1 + 60; }
					if (endInstanceVar2 < startInstanceVar2) {
						endInstanceVar2 = endInstanceVar2 + 60;
						endInstanceVar1 = endInstanceVar1 - 1;
					}
					var calcInstanceVar = ((((endInstanceVar1 - startInstanceVar1) * 60) + (endInstanceVar2 - startInstanceVar2)) * 1000);
					_timeInInstance = new Common.Helpers.WaitTimer(new TimeSpan(0, 0, 0, 0, calcInstanceVar));
					var timeInInstanceAsString = UtilBuildTimeAsString(_timeInInstance.WaitTime);
					QBCLog.Info("Your instance run took " + timeInInstanceAsString);
					if (calcInstanceVar >= WaitTime) { WaitTime = 0; }
					if (calcInstanceVar < WaitTime)
					{
						WaitTime = WaitTime - calcInstanceVar;
						_timeInInstance = new Common.Helpers.WaitTimer(new TimeSpan(0, 0, 0, 0, WaitTime));
						timeInInstanceAsString = UtilBuildTimeAsString(_timeInInstance.WaitTime);
						QBCLog.Info("Waiting for " + timeInInstanceAsString);
					}
				}
				_timer = new Common.Helpers.WaitTimer(new TimeSpan(0, 0, 0, 0, WaitTime));
				_waitTimeAsString = UtilBuildTimeAsString(_timer.WaitTime);
				_timer.Reset();
			}
		}
		#endregion
	}
}
